/*
 * $Id: Main.cc 377 2009-12-02 05:17:14Z chunghh $
 */

#include "Globals.h"
#include <xp/bas/string.h>
#include <xp/bas/locale.h>
#include <xp/bas/memory.h>
#include <xp/bas/stdcli.h>

static xp_cli_t* parse_cli (int argc, xp_char_t* argv[]);
static void set_log_path (const xp_char_t* path);
static int __xp_main (int argc, xp_char_t* argv[]);

extern "C" { int xp_main (int argc, xp_char_t* argv[]); };

int xp_main (int argc, xp_char_t* argv[])
{
	int n;

	if (xp_log_open (&app_log, 
		APP_NAME, XP_LOG_CONSOLE | XP_LOG_DEBUG) == XP_NULL) {
		xp_fprintf (xp_stderr, XP_T("cannot open log. aborting"));
                n = -1;
	}
	else {
		n = __xp_main (argc, argv);
		xp_log_close (&app_log);
	}

	return n;
}

static void print_usage (const xp_cli_t* cli)
{
	XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("<usage> %s [options]"), cli->verb);
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("Options as follows:"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("--foreground      run the program in the foreground mode"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("--sysconfdir=path specify the directory to store system configuration"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("--logfile=path    specify the log file"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("--tcpgateservice=string"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("                  specify the service name of a TCP control channel"));
	XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
		XP_T("--chroot=path     speficy the special root"));
}

static int __xp_main (int argc, xp_char_t* argv[])
{
	xp_cli_t* cli;

	/* initialization */
	if (xp_setlocale () == -1) {
		XP_LOG0 (&app_log, XP_NULL,
			XP_LOG_CRITICAL, XP_T("cannot set locale"));
		return -1;
	}

	if (app_path.setToSelf (argv[0]) == -1) {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL,
			XP_T("cannot get path for %s. aborting"), argv[0]);
		return -1;
	}

#ifdef XP_CHAR_IS_MCHAR
	xp_strxcpy (app_mdir, xp_countof(app_mdir), app_path.baseDir());
#else	
	xp_wcstomcs (app_path.baseDir(), app_mdir, xp_countof(app_mdir));
#endif

	/* parse command line */
	if ((cli = parse_cli (argc, argv)) == XP_NULL) return -1;
	if (xp_getncliparams(cli) > 0) 
	{
		print_usage (cli);
		XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
			XP_T("<error> redundant cli parameters"));
		return -1;
	}

	xp_char_t* root = xp_getclioptval(cli, XP_T("chroot"));
	if (root) 
	{
		if (xp_chroot (root) == -1)
		{
			XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL,
				XP_T("<error> failed to chroot to %s"), root);
			return -1;
		}
	}
	
	set_log_path (xp_getclioptval(cli, XP_T("logfile")));

	if (xp_getclioptval(cli, XP_T("foreground")) != XP_NULL) {
		app_mode = APP_MODE_FOREGROUND;
	}
	xp_clearcli (cli);


	/* begin main logic */
	int ret_code;
	try 
	{
		app_main = new MainApp ();

		app_main->setSysconfDir (xp_getclioptval(cli, XP_T("sysconfdir")));
		app_main->forceTcpGateService (xp_getclioptval(cli, XP_T("tcpgateservice")));

		int err_code;
		if ((!(app_mode & APP_MODE_FOREGROUND) && app_main->start(&err_code) == -1) ||
		    ((app_mode & APP_MODE_FOREGROUND) && app_main->startAsNonDaemon(&err_code) == -1)) 
		{
			XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL,
				XP_T("application startup failure, aborting - %d"),
				err_code);
			ret_code = -1;
		}
		else ret_code = app_main->returnCode ();

		delete app_main;
		app_main = XP_NULL;
	}
	catch (Exception& ex) 
	{
		if (app_main) 
		{
			delete app_main;
			app_main = XP_NULL;
		}
		
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL,
			XP_T("application execption caught, aborting - %s"), 
			XP_EXCEPTION_MSG(ex));
		ret_code = -1;
	}
	catch (...) 
	{
		if (app_main) 
		{
			delete app_main;
			app_main = XP_NULL;
		}
		XP_LOG0 (&app_log, XP_NULL, XP_LOG_CRITICAL,
			XP_T("unknown application execption caught, aborting"));
		ret_code = -1;
	}

	return ret_code;
}

static int handle_cli_error (
	const xp_cli_t* cli, int code, 
	const xp_char_t* name, const xp_char_t* value)
{

	print_usage (cli);

	if (code == XP_CLI_ERROR_INVALID_OPTNAME) {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> unknown option - %s"), name);
	}
	else if (code == XP_CLI_ERROR_MISSING_OPTNAME) {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> missing option - %s"), name);
	}
	else if (code == XP_CLI_ERROR_REDUNDANT_OPTVAL) {
		XP_LOG2 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> redundant value %s for %s"), value, name);
	}
	else if (code == XP_CLI_ERROR_MISSING_OPTVAL) {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> missing value for %s"), name);
	}
	else if (code == XP_CLI_ERROR_MEMORY) {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> memory error in processing %s"), name);
	}
	else {
		XP_LOG1 (&app_log, XP_NULL, XP_LOG_CRITICAL, 
			XP_T("<error> unknown cli error - %d"), code);
	}

	return -1;
}

static xp_cli_t* parse_cli (int argc, xp_char_t* argv[])
{
	static const xp_char_t* optsta[] = 
	{
		XP_T("--"), XP_NULL
	};

	static xp_cliopt_t opts[] = 
	{
		{ XP_T("foreground"),     0 },
		{ XP_T("sysconfdir"),     XP_CLI_OPTVAL },
		{ XP_T("logfile"),        XP_CLI_OPTVAL },
		{ XP_T("tcpgateservice"), XP_CLI_OPTVAL },
		{ XP_T("chroot"),         XP_CLI_OPTVAL },
		{ XP_NULL,                0 }
	};

	static xp_cli_t cli = 
	{
		handle_cli_error,
		optsta,
		XP_T("="),
		opts
	};

	if (xp_parsecli (argc, argv, &cli) == -1) return XP_NULL;
	return &cli;
}

void set_log_path (const xp_char_t* path)
{
	xp_char_t pbuf[XP_PATH_MAX + 1];

	if (path == XP_NULL) 
	{
		xp_strxapp (
			pbuf, XP_COUNTOF(pbuf), 
			XP_T(APP_LOG_DIR), XP_T("/"), APP_LOG_FILE, XP_NULL);
		xp_log_settargetfile (&app_log, pbuf);
	}
	else xp_log_settargetfile (&app_log, path);
}

